package scaladuct.memento

import scaladuct._
import l10n.{HasHintsMetaData, ResourceBundleHasHintsMetaData, DuplexHasHintsMetaData, DecoratingMetaDataSource}
import java.util.{Locale, ResourceBundle}
import reflect.{PropertyInfoDecorator, DefaultPropertyInfo, PropertyInfo, CookieReflection}
import LookupFunctions._
import java.lang.String

/**
 * <p>
 * Created by IntelliJ IDEA.
 * User: zslajchrt
 * Date: Dec 5, 2010
 * Time: 1:32:58 PM
 */

trait MementoPlayingGateway[X] extends GatewayWithHints[X]
        with MementoGatewayBase
{
  case object MementoPlayerMenu extends CompactProperty

  private[this] val config = new MementoPlayerConfig
  private[this] var memento: Memento = null

  override def hints(subject: String): List[Property[Any]] = {
    MementoPlayerMenu :: super.hints(subject)
  }

  override protected def decoratePage(page: ConfigPage[X]): ConfigPage[X] = {
    val decoratedPage: ConfigPage[X] = super.decoratePage(page)
    new DecoratedPage[X](decoratedPage)
  }

  override def hintPage(hint: Property[Any]): ConfigPage[Any] = {
    hint match {
      case MementoPlayerMenu => {
        val setter: Function2[MementoPlayerConfig, Memento, Unit] = (newConfig, mem) => {
          BeanUtils.copyBean(newConfig, config)
          memento = mem
        }
        PhaseMethodInterceptor[Any](classOf[MementoPlayerMenuStarter], BeanUtils.createClone(config),
          mementoStorage, setter).page
      }
      case _ => super.hintPage(hint)
    }
  }

  override def getCookie(): AnyRef = {
    if (config.play) {
      assume(memento != null)
      memento.cookieMap.get(id) match {
        case None => super.getCookie
        case Some(recordedCookie) => {
          if (noParameterInRecordedCookie())
            Terminal // force the short-circuit
          else
            recordedCookie
        }
      }
    } else super.getCookie
  }

  private def noParameterInRecordedCookie(): Boolean = {
    //val pageIdPrefix = SelectedParameterKey(id, "")
    !memento.parameterProperties.keys.exists(_.pageId.startsWith(id))
  }

  override def consumeCookie(cookie: AnyRef) = {
    val appliedCookie = if (config.play)
      memento.cookieMap.get(id) match {
        case None => cookie
        case Some(recordedCookie) => if (noParameterInRecordedCookie())
          recordedCookie
        else
          cookie // parameterized recorded cookie
      }
    else cookie
    super.consumeCookie(appliedCookie)
  }

  @SerialVersionUID(1L)
  class DecoratedPage[X](decPage: ConfigPage[X]) extends ConfigPageDecorator[X](decPage)
          with CookieReflection
          with DecoratingMetaDataSource {
    val extendingBundlePrefix = ""
    val extendingBundleName = classOf[MementoPlayingGateway[X]].getName

    def propertyInfo[X](property: Property[X]): PropertyInfo[X] = {
      val absolutePropertyKey = SelectedParameterKey(decPage.id, property.name)
      val editableProperty = if (memento != null && config.play)
        memento.parameterProperties.contains(absolutePropertyKey)
      else
        true

      decPage.extension(classOf[CookieReflection]) match {
        case Some(ext) => {
          new PropertyInfoDecorator[X](ext.propertyInfo(property)) {
            override def mandatory = editableProperty

            override def enabled = editableProperty
          }
        }
        case None => DefaultPropertyInfo[X](property, editableProperty, editableProperty)
      }
    }

  }

}

@serializable @SerialVersionUID(1L)
class MementoPlayerConfig {
  var play: Boolean = false
  var mementoName: String = null
}

@serializable @SerialVersionUID(1L)
class MementoPlayerMenuStarter(config: MementoPlayerConfig, storage: MementoStorage,
                               setter: (MementoPlayerConfig, Memento) => Unit) extends Starter[Null] {
  config.play = true

  def newInstance() = phase1(config)

  def phase1(config: MementoPlayerConfig): Null = {
    if (config.play) {
      storage.findMemento(config.mementoName) match {
        case None => error("Memento '" + config.mementoName + "' not found")
        case Some(memento) => {
          setter(config, memento)
        }
      }
    }

    null
  }

  def destroy(instance: Null) = null
}


@serializable @SerialVersionUID(1L)
object Memento {
  def absolutePropertyId(getCurrentPageId: String, propertyName: String): String = {
    getCurrentPageId + "#" + propertyName
  }
}

@serializable @SerialVersionUID(1L)
class Memento(val name: String,
              val cookieMap: Map[String, AnyRef],
              val parameterProperties: Map[SelectedParameterKey, ParameterProperty]
        )